// =============================
// 20.3 启用/禁用函数模板
// =============================

为什么我们需要有选择地启用或禁用模板，而不是简单地"总是启用"所有模板。

1. 类型安全性
1.1问题：模板可以为任何类型实例化，即使这些类型不满足必要的操作要求。
template <typename T>
T add(T a, T b) {
    return a + b;  // 假设T支持+操作符
}

// 如果用户尝试：
struct NoAddOperator {};
add(NoAddOperator{}, NoAddOperator{});  // 编译错误，但错误消息可能很复杂

1.2解决方案：通过启用/禁用技术，我们可以明确限制模板只接受满足特定要求的类型：
template <typename T>
std::enable_if_t<has_addition_operator<T>::value, T>
add(T a, T b) {
    return a + b;
}

2. 重载解析控制
2.1问题：当有多个模板重载时，编译器可能会选择"错误的"重载或产生歧义。
// 通用版本
template <typename T>
void process(T value) { /* 通用实现 */ }

// 整数特化版本
template <typename T>
void process(T value) { /* 整数特化实现 */ }  // 编译错误：重复定义

2.2解决方案：使用启用/禁用技术来区分重载：
// 通用版本
template <typename T, 
          typename = std::enable_if_t<!std::is_integral_v<T>>>
void process(T value) { /* 通用实现 */ }

// 整数特化版本
template <typename T,
          typename = std::enable_if_t<std::is_integral_v<T>>>
void process(T value) { /* 整数特化实现 */ }

3. 更好的错误消息
3.1问题：当模板实例化失败时，错误消息通常很复杂且难以理解。
3.2解决方案：通过静态断言和启用/禁用技术，可以提供更清晰的错误消息：
template <typename T>
void serialize(T value) {
    static_assert(is_serializable<T>::value, 
                 "Type must be serializable");
    // 实现...
}


4. 编译优化
4.1问题：不必要的模板实例化会增加编译时间和生成的代码大小。
4.2解决方案：只为真正需要的类型实例化模板：
template <typename T>
std::enable_if_t<std::is_pod_v<T>>
fast_copy(T* dest, const T* src, size_t count) {
    std::memcpy(dest, src, count * sizeof(T));
}

template <typename T>
std::enable_if_t<!std::is_pod_v<T>>
fast_copy(T* dest, const T* src, size_t count) {
    for (size_t i = 0; i < count; ++i) {
        dest[i] = src[i];
    }
}

